Included templates

contentlayer-next-docs-page-with-nav

contentlayer-next-docs-page-with-nav

Nav between documents and on page heading elements

Pasted files structure

├── app
│   └── docs
│       └── [[...slug]]
│           ├── layout.tsx
│           └── page.tsx
├── components
│   ├── DocsSideNav.tsx
│   └── PageContentNav.tsx
├── docs
│   ├── Basic use
│   │   ├── Save and Paste.mdx
│   │   └── Scripts.mdx
│   ├── Included scripts
│   │   ├── Add-Contentlayer-docs-nav-template-TS.mdx
│   │   ├── Add-Next-Auth.js-TS.mdx
│   │   ├── Add-Next-RTK-TS.mdx
│   │   ├── Add-ShadcnUI-TS.mdx
│   │   └── Create-Next-ShadcnUI-TS.mdx
│   └── Included templates
│       ├── contentlayer-basic-styled-next-mdx-components.mdx
│       ├── contentlayer-config.mdx
│       ├── contentlayer-next-docs-page-with-nav.mdx
│       ├── contentlayer-next-styled-mdx-components.mdx
│       ├── next-redux-ts.mdx
│       ├── next-shad-darkmode.mdx
│       ├── next13-app-auth-paste-from-root.mdx
│       └── redux-ts.mdx
└── styles
    └── docs.css
├── app
│   └── docs
│       └── [[...slug]]
│           ├── layout.tsx
│           └── page.tsx
├── components
│   ├── DocsSideNav.tsx
│   └── PageContentNav.tsx
├── docs
│   ├── Basic use
│   │   ├── Save and Paste.mdx
│   │   └── Scripts.mdx
│   ├── Included scripts
│   │   ├── Add-Contentlayer-docs-nav-template-TS.mdx
│   │   ├── Add-Next-Auth.js-TS.mdx
│   │   ├── Add-Next-RTK-TS.mdx
│   │   ├── Add-ShadcnUI-TS.mdx
│   │   └── Create-Next-ShadcnUI-TS.mdx
│   └── Included templates
│       ├── contentlayer-basic-styled-next-mdx-components.mdx
│       ├── contentlayer-config.mdx
│       ├── contentlayer-next-docs-page-with-nav.mdx
│       ├── contentlayer-next-styled-mdx-components.mdx
│       ├── next-redux-ts.mdx
│       ├── next-shad-darkmode.mdx
│       ├── next13-app-auth-paste-from-root.mdx
│       └── redux-ts.mdx
└── styles
    └── docs.css

Files contents

./app/docs/[[...slug]]/layout.tsx
import DocsSideNav from "@/components/DocsSideNav";
import PageContentNav from "@/components/PageContentNav";
import '../../../styles/docs.css'
 
export default function DocsLayout({ children }: { children: React.ReactNode }) {
    return (
        <div className="grid grid-cols-[1fr_3fr_1fr] max-w-screen-xl mx-auto mt-10 relative mb-32">
            <DocsSideNav />
            {children}
            <PageContentNav />
        </div>
    )
}
./app/docs/[[...slug]]/layout.tsx
import DocsSideNav from "@/components/DocsSideNav";
import PageContentNav from "@/components/PageContentNav";
import '../../../styles/docs.css'
 
export default function DocsLayout({ children }: { children: React.ReactNode }) {
    return (
        <div className="grid grid-cols-[1fr_3fr_1fr] max-w-screen-xl mx-auto mt-10 relative mb-32">
            <DocsSideNav />
            {children}
            <PageContentNav />
        </div>
    )
}
./app/docs/[[...slug]]/page.tsx
import { allDocs } from "@/.contentlayer/generated"
import { Mdx } from "@/components/mdx-components"
import { redirect } from 'next/navigation'
import { MdKeyboardArrowRight } from 'react-icons/md'
 
const getDocBySlugs = (slugs: string[]) => {
    const pathFromSlugs = slugs.join('/')
    const doc = allDocs.find((doc) => doc._raw.flattenedPath === pathFromSlugs)
    if (!doc) return redirect('/docs/Basic use/Save and Paste')
    return doc
}
 
export default function DocsPage({ params }: { params: { slug?: string[] } }) {
 
    if (!params.slug) return redirect('/docs/Basic use/Save and Paste')
    params.slug = params.slug?.map((slug) => decodeURI(slug))
 
    const doc = getDocBySlugs(params.slug)
 
    return (
        <article className="min-h-screen pb-8 overflow-x-auto px-8 border-l">
            <div className="flex mb-4 gap-1 items-center text-sm">
                <p className="text-muted-foreground">{doc._raw.sourceFileDir}</p>
                <MdKeyboardArrowRight className="text-muted-foreground" />
                <p className="font-medium text-indigo-800 dark:bg-zinc-900 dark:text-indigo-600 bg-indigo-100 px-2 rounded-full">{doc.title}</p>
            </div>
            <Mdx code={doc?.body.code!} />
        </article>
    )
}
./app/docs/[[...slug]]/page.tsx
import { allDocs } from "@/.contentlayer/generated"
import { Mdx } from "@/components/mdx-components"
import { redirect } from 'next/navigation'
import { MdKeyboardArrowRight } from 'react-icons/md'
 
const getDocBySlugs = (slugs: string[]) => {
    const pathFromSlugs = slugs.join('/')
    const doc = allDocs.find((doc) => doc._raw.flattenedPath === pathFromSlugs)
    if (!doc) return redirect('/docs/Basic use/Save and Paste')
    return doc
}
 
export default function DocsPage({ params }: { params: { slug?: string[] } }) {
 
    if (!params.slug) return redirect('/docs/Basic use/Save and Paste')
    params.slug = params.slug?.map((slug) => decodeURI(slug))
 
    const doc = getDocBySlugs(params.slug)
 
    return (
        <article className="min-h-screen pb-8 overflow-x-auto px-8 border-l">
            <div className="flex mb-4 gap-1 items-center text-sm">
                <p className="text-muted-foreground">{doc._raw.sourceFileDir}</p>
                <MdKeyboardArrowRight className="text-muted-foreground" />
                <p className="font-medium text-indigo-800 dark:bg-zinc-900 dark:text-indigo-600 bg-indigo-100 px-2 rounded-full">{doc.title}</p>
            </div>
            <Mdx code={doc?.body.code!} />
        </article>
    )
}
./components/DocsSideNav.tsx
'use client'
 
import { usePathname } from 'next/navigation'
import { Doc, allDocs } from "@/.contentlayer/generated";
import Link from "next/link";
import { cn } from '@/lib/utils';
 
 
export default function DocsSideNav() {
    const sortedArr = allDocs.sort((a, b) => a.sortNum - b.sortNum)
    const path = decodeURI(usePathname())
 
    const categorizedDocs = sortedArr.reduce((acc: { [key: Doc['_raw']['sourceFileDir']]: Doc[] }, doc) => {
        const currCatContent = acc[doc._raw.sourceFileDir]
        return {
            ...acc,
            [doc._raw.sourceFileDir]: [...(currCatContent ? currCatContent : []), doc]
        }
    }, {})
 
    return (
        <div className="relative pr-6">
            <div className='sticky top-[7.04rem] text-sm'>
                {Object.entries(categorizedDocs).map(([cat, docs], i) => {
                    return (
                        <div key={i} className="mb-4">
                            <p className="text-foreground font-semibold mb-2">{cat}</p>
                            <div className="flex flex-col gap-1">
                                {docs.map((doc, j) => {
                                    const isActive = path === `/docs/${doc._raw.flattenedPath}`
 
                                    return <Link key={j} style={{ contain: "inline-size" }} href={`/docs/${doc._raw.flattenedPath}`} className={cn("ml-0 text-muted-foreground hover:text-indigo-700 dark:hover:text-indigo-600 transition-all hover:bg-indigo-50 py-1 px-2 rounded-md text-ellipsis whitespace-nowrap overflow-hidden dark:hover:bg-zinc-900", isActive ? "bg-indigo-50 dark:bg-zinc-900 dark:text-indigo-600 text-indigo-700" : "")}>{doc.title}</Link>
                                })}
                            </div>
                        </div>
                    )
                })}
            </div>
        </div>
    )
}
./components/DocsSideNav.tsx
'use client'
 
import { usePathname } from 'next/navigation'
import { Doc, allDocs } from "@/.contentlayer/generated";
import Link from "next/link";
import { cn } from '@/lib/utils';
 
 
export default function DocsSideNav() {
    const sortedArr = allDocs.sort((a, b) => a.sortNum - b.sortNum)
    const path = decodeURI(usePathname())
 
    const categorizedDocs = sortedArr.reduce((acc: { [key: Doc['_raw']['sourceFileDir']]: Doc[] }, doc) => {
        const currCatContent = acc[doc._raw.sourceFileDir]
        return {
            ...acc,
            [doc._raw.sourceFileDir]: [...(currCatContent ? currCatContent : []), doc]
        }
    }, {})
 
    return (
        <div className="relative pr-6">
            <div className='sticky top-[7.04rem] text-sm'>
                {Object.entries(categorizedDocs).map(([cat, docs], i) => {
                    return (
                        <div key={i} className="mb-4">
                            <p className="text-foreground font-semibold mb-2">{cat}</p>
                            <div className="flex flex-col gap-1">
                                {docs.map((doc, j) => {
                                    const isActive = path === `/docs/${doc._raw.flattenedPath}`
 
                                    return <Link key={j} style={{ contain: "inline-size" }} href={`/docs/${doc._raw.flattenedPath}`} className={cn("ml-0 text-muted-foreground hover:text-indigo-700 dark:hover:text-indigo-600 transition-all hover:bg-indigo-50 py-1 px-2 rounded-md text-ellipsis whitespace-nowrap overflow-hidden dark:hover:bg-zinc-900", isActive ? "bg-indigo-50 dark:bg-zinc-900 dark:text-indigo-600 text-indigo-700" : "")}>{doc.title}</Link>
                                })}
                            </div>
                        </div>
                    )
                })}
            </div>
        </div>
    )
}
./components/PageContentNav.tsx
'use client'
 
import { cn } from "@/lib/utils"
import Link from "next/link"
import { useEffect, useState } from "react"
import { Separator } from "./ui/separator"
import { FaStar } from 'react-icons/fa'
import { BsStar } from 'react-icons/bs'
import { HiArrowLongRight } from 'react-icons/hi2'
 
 
export default function PageContentNav() {
    const [linkedHeadings, setLinkedHeadings] = useState<HTMLAnchorElement[]>([])
    const [highlitedHeading, setHighlitedHeading] = useState<HTMLAnchorElement | null>(null)
 
    useEffect(() => {
        const lHeadings = document.querySelectorAll(`[aria-label="Link to section"]`)
        const mainNav = document.getElementById('main-nav')
        console.log("mainNav", mainNav)
        lHeadings.forEach((heading) => {
            const observer = new IntersectionObserver((entries) => {
                console.log("ee", entries)
                entries.forEach((entry) => {
                    if (entry.isIntersecting) {
                        setHighlitedHeading(heading as HTMLAnchorElement)
                    }
                })
            }, { root: null, rootMargin: "0px 0px -80% 0px", threshold: 1 })
            observer.observe(heading)
        })
        setLinkedHeadings(Array.from(lHeadings) as HTMLAnchorElement[])
    }, [])
 
    return (
        <div style={{ contain: 'strict' }}>
            <div className="sticky top-[7.04rem]">
                <h3 className="text-sm font-semibold mb-2">On this page</h3>
                <div className=" flex flex-col gap-1">
                    {linkedHeadings?.map((heading, i) => {
                        return (
                            <div key={i} className=" w-full">
                                <a href={heading.href} className={cn("text-sm text-muted-foreground hover:text-indigo-700 transition-all hover:bg-indigo-50 dark:hover:bg-zinc-900 dark:hover:text-indigo-600 py-1 px-2 rounded-md whitespace-nowrap w-full block", heading.href === highlitedHeading?.href ? "text-indigo-700 dark:bg-zinc-900 dark:text-indigo-600 bg-indigo-50" : "")}>
                                    <p className="whitespace-nowrap text-ellipsis overflow-hidden">
                                        {heading.innerText}
                                    </p>
                                </a>
                            </div>
                        )
                    })}
                </div>
                <Separator className="mt-6 mb-6" />
                <div className="text-muted-foreground text-xs font-medium flex flex-col gap-2 pl-2">
                    <Link href={'https://github.com/xDepcio/strapup'}>
                        <div className="flex items-center gap-2 hover:text-indigo-800 dark:hover:text-indigo-600 transition-all">
                            <p>Star us</p>
                            <BsStar />
                        </div>
                    </Link>
                    <Link href={'https://github.com/xDepcio/strapup'}>
                        <div className="flex items-center gap-2 hover:text-indigo-800 dark:hover:text-indigo-600 transition-all">
                            <p>Contribute</p>
                            <HiArrowLongRight />
                        </div>
                    </Link>
                </div>
            </div>
        </div>
    )
}
./components/PageContentNav.tsx
'use client'
 
import { cn } from "@/lib/utils"
import Link from "next/link"
import { useEffect, useState } from "react"
import { Separator } from "./ui/separator"
import { FaStar } from 'react-icons/fa'
import { BsStar } from 'react-icons/bs'
import { HiArrowLongRight } from 'react-icons/hi2'
 
 
export default function PageContentNav() {
    const [linkedHeadings, setLinkedHeadings] = useState<HTMLAnchorElement[]>([])
    const [highlitedHeading, setHighlitedHeading] = useState<HTMLAnchorElement | null>(null)
 
    useEffect(() => {
        const lHeadings = document.querySelectorAll(`[aria-label="Link to section"]`)
        const mainNav = document.getElementById('main-nav')
        console.log("mainNav", mainNav)
        lHeadings.forEach((heading) => {
            const observer = new IntersectionObserver((entries) => {
                console.log("ee", entries)
                entries.forEach((entry) => {
                    if (entry.isIntersecting) {
                        setHighlitedHeading(heading as HTMLAnchorElement)
                    }
                })
            }, { root: null, rootMargin: "0px 0px -80% 0px", threshold: 1 })
            observer.observe(heading)
        })
        setLinkedHeadings(Array.from(lHeadings) as HTMLAnchorElement[])
    }, [])
 
    return (
        <div style={{ contain: 'strict' }}>
            <div className="sticky top-[7.04rem]">
                <h3 className="text-sm font-semibold mb-2">On this page</h3>
                <div className=" flex flex-col gap-1">
                    {linkedHeadings?.map((heading, i) => {
                        return (
                            <div key={i} className=" w-full">
                                <a href={heading.href} className={cn("text-sm text-muted-foreground hover:text-indigo-700 transition-all hover:bg-indigo-50 dark:hover:bg-zinc-900 dark:hover:text-indigo-600 py-1 px-2 rounded-md whitespace-nowrap w-full block", heading.href === highlitedHeading?.href ? "text-indigo-700 dark:bg-zinc-900 dark:text-indigo-600 bg-indigo-50" : "")}>
                                    <p className="whitespace-nowrap text-ellipsis overflow-hidden">
                                        {heading.innerText}
                                    </p>
                                </a>
                            </div>
                        )
                    })}
                </div>
                <Separator className="mt-6 mb-6" />
                <div className="text-muted-foreground text-xs font-medium flex flex-col gap-2 pl-2">
                    <Link href={'https://github.com/xDepcio/strapup'}>
                        <div className="flex items-center gap-2 hover:text-indigo-800 dark:hover:text-indigo-600 transition-all">
                            <p>Star us</p>
                            <BsStar />
                        </div>
                    </Link>
                    <Link href={'https://github.com/xDepcio/strapup'}>
                        <div className="flex items-center gap-2 hover:text-indigo-800 dark:hover:text-indigo-600 transition-all">
                            <p>Contribute</p>
                            <HiArrowLongRight />
                        </div>
                    </Link>
                </div>
            </div>
        </div>
    )
}
./docs/Basic use/Save and Paste.mdx
---
title: Save and Paste
sortNum: 1
---
#### How to run It?
To run the Strapup CLI from anywhere on your system, just use:
```command
npx strapup
./docs/Basic use/Save and Paste.mdx
---
title: Save and Paste
sortNum: 1
---
#### How to run It?
To run the Strapup CLI from anywhere on your system, just use:
```command
npx strapup

This will install required dependencies and correctly setup your enviroment on the first run.

Running for the first time

When you run Strapup for the first time, it will need to perform some setup. For this, you will be asked to provide:

  • Path where to create Strapup directory - used to store templates and scripts.

Strapup saves those values in enviroment variables, so on next runs you don't have to provide them again.

Saving and pasting templates

You can save and create templates from within interactive CLI (npx strapup).

When you paste a template, you will be asked to:

  • Choose a template.
  • Provide path where to paste - template content will be paste there. Must be a relative path to a directory. If a directory doesn't exist, strapup will be created.

When you save a template, you will be asked to provide:

  • Template name and description - used solely for identification purpose.
  • Relative path or paths to source directories or files.
  • Choose additional options:
    • with gitignore - If selected, files ignored by git will not be included in the template.

Next steps

```mdx title="./docs/Basic use/Scripts.mdx"
---
title: Scripts
sortNum: 2
---

#### Introduction
Scripts in Strapup are just lists of strings representing shell commands. These commands can also be Strapup instructions like `paste` and `save`. These feature make them very powerfull and can help you automate the process of setting up your projects.

Take one of built-in scripts `nextJsShadcnAuthJs` for example. It setups NextJS 13 app with shadcnUI and Auth.js. You can run It from Strapup CLI (`npx strapup`). Under the hood it will run these commands:

```bash {"1"}
npx create-next-app ${projectName} --typescript --tailwind --eslint --app --no-src-dir --import-alias "@/*"
cd ${projectName}
npx shadcn-ui@latest init -y
npx shadcn-ui@latest add button -y
npx shadcn-ui@latest add input -y
npx shadcn-ui@latest add skeleton -y
npm i next-auth
npx strapup paste next13-app-auth-paste-from-root ./
```mdx title="./docs/Basic use/Scripts.mdx"
---
title: Scripts
sortNum: 2
---

#### Introduction
Scripts in Strapup are just lists of strings representing shell commands. These commands can also be Strapup instructions like `paste` and `save`. These feature make them very powerfull and can help you automate the process of setting up your projects.

Take one of built-in scripts `nextJsShadcnAuthJs` for example. It setups NextJS 13 app with shadcnUI and Auth.js. You can run It from Strapup CLI (`npx strapup`). Under the hood it will run these commands:

```bash {"1"}
npx create-next-app ${projectName} --typescript --tailwind --eslint --app --no-src-dir --import-alias "@/*"
cd ${projectName}
npx shadcn-ui@latest init -y
npx shadcn-ui@latest add button -y
npx shadcn-ui@latest add input -y
npx shadcn-ui@latest add skeleton -y
npm i next-auth
npx strapup paste next13-app-auth-paste-from-root ./

projectName is entered by user before commands are executed.
Take a look at the last line, which pastes Strapup template to root directory. One script can paste multiple templates or run other Strapup scripts, thus making it possible to create complex projects with one command.

Creating scripts

Scripts can be modified and added by editing scripts.mjs file located in Strapup directory. You can see its path whenever your running npx strapup.

Take a look at scripts.mjs contents:

export const scripts = {
    nextJsShadcnAuthJs: {
        description: "Create new Next.js typescript, app dir, tailwind, no src dir, shadcn, basic Auth.js",
        command: (projectName) => [
            `npx create-next-app ${projectName} --typescript --tailwind --eslint --app --no-src-dir --import-alias "@/*"`,
            `cd ${projectName}`,
            `npx shadcn-ui@latest init -y`,
            `npx shadcn-ui@latest add button -y`,
            `npx shadcn-ui@latest add input -y`,
            `npx shadcn-ui@latest add skeleton -y`,
            `npm i next-auth`,
            `strapup paste next13-app-auth-paste-from-root ./`,
        ]
    },
    // Create a new script by adding a key-value pair based on examples above.
}
export const scripts = {
    nextJsShadcnAuthJs: {
        description: "Create new Next.js typescript, app dir, tailwind, no src dir, shadcn, basic Auth.js",
        command: (projectName) => [
            `npx create-next-app ${projectName} --typescript --tailwind --eslint --app --no-src-dir --import-alias "@/*"`,
            `cd ${projectName}`,
            `npx shadcn-ui@latest init -y`,
            `npx shadcn-ui@latest add button -y`,
            `npx shadcn-ui@latest add input -y`,
            `npx shadcn-ui@latest add skeleton -y`,
            `npm i next-auth`,
            `strapup paste next13-app-auth-paste-from-root ./`,
        ]
    },
    // Create a new script by adding a key-value pair based on examples above.
}

It exports POJO object where keys - are scripts names, and values - script informations:

  • description - string displayed in Strapup CLI for identification.
  • command - function, which returns array of strings, which will be executed as shell commands. This function can have any number of parameters and user will be automatically prompted to enter them before script is executed. Parameter name will be displayed in CLI in the prompt.

Next steps

```mdx title="./docs/Included scripts/Add-Contentlayer-docs-nav-template-TS.mdx"
---
title: Add-Contentlayer-docs-nav-template-TS
sortNum: 1004
---
### Add-Contentlayer-docs-nav-template-TS
Add working contentlayer MDX template to existing NextJS project
### Executed commands
```bash
npm i contentlayer next-contentlayer react-icons react-hot-toast remark-gfm rehype-slug rehype-autolink-headings rehype-pretty-code
npx strapup paste contentlayer-config ./
npx strapup paste contentlayer-next-styled-mdx-components ./
npx strapup paste contentlayer-next-docs-page-with-nav ./
```mdx title="./docs/Included scripts/Add-Contentlayer-docs-nav-template-TS.mdx"
---
title: Add-Contentlayer-docs-nav-template-TS
sortNum: 1004
---
### Add-Contentlayer-docs-nav-template-TS
Add working contentlayer MDX template to existing NextJS project
### Executed commands
```bash
npm i contentlayer next-contentlayer react-icons react-hot-toast remark-gfm rehype-slug rehype-autolink-headings rehype-pretty-code
npx strapup paste contentlayer-config ./
npx strapup paste contentlayer-next-styled-mdx-components ./
npx strapup paste contentlayer-next-docs-page-with-nav ./
```mdx title="./docs/Included scripts/Add-Next-Auth.js-TS.mdx"
---
title: Add-Next-Auth.js-TS
sortNum: 1003
---
### Add-Next-Auth.js-TS
To existing next app, add basic next-auth
### Executed commands
```bash
npm i next-auth
npx strapup paste next13-app-auth-paste-from-root ./
```mdx title="./docs/Included scripts/Add-Next-Auth.js-TS.mdx"
---
title: Add-Next-Auth.js-TS
sortNum: 1003
---
### Add-Next-Auth.js-TS
To existing next app, add basic next-auth
### Executed commands
```bash
npm i next-auth
npx strapup paste next13-app-auth-paste-from-root ./
```mdx title="./docs/Included scripts/Add-Next-RTK-TS.mdx"
---
title: Add-Next-RTK-TS
sortNum: 1001
---
### Add-Next-RTK-TS
Add TS RTK to existing NextJS app
### Executed commands
```bash
npm i @reduxjs/toolkit react-redux
npx strapup paste next-redux-ts ${pasteDir}
```mdx title="./docs/Included scripts/Add-Next-RTK-TS.mdx"
---
title: Add-Next-RTK-TS
sortNum: 1001
---
### Add-Next-RTK-TS
Add TS RTK to existing NextJS app
### Executed commands
```bash
npm i @reduxjs/toolkit react-redux
npx strapup paste next-redux-ts ${pasteDir}
```mdx title="./docs/Included scripts/Add-ShadcnUI-TS.mdx"
---
title: Add-ShadcnUI-TS
sortNum: 1002
---
### Add-ShadcnUI-TS
To existing next app, Shadcn UI add (button, input, skeleton, dropdown), darkmode
### Executed commands
```bash
npx shadcn-ui@latest init -y
npx shadcn-ui@latest add button -y
npx shadcn-ui@latest add input -y
npx shadcn-ui@latest add skeleton -y
npx shadcn-ui@latest add dropdown-menu -y
npm i next-themes
npx strapup paste next-shad-darkmode ./
```mdx title="./docs/Included scripts/Add-ShadcnUI-TS.mdx"
---
title: Add-ShadcnUI-TS
sortNum: 1002
---
### Add-ShadcnUI-TS
To existing next app, Shadcn UI add (button, input, skeleton, dropdown), darkmode
### Executed commands
```bash
npx shadcn-ui@latest init -y
npx shadcn-ui@latest add button -y
npx shadcn-ui@latest add input -y
npx shadcn-ui@latest add skeleton -y
npx shadcn-ui@latest add dropdown-menu -y
npm i next-themes
npx strapup paste next-shad-darkmode ./
```mdx title="./docs/Included scripts/Create-Next-ShadcnUI-TS.mdx"
---
title: Create-Next-ShadcnUI-TS
sortNum: 1000
---
### Create-Next-ShadcnUI-TS
New Next.js app, Shadcn UI add (button, input, skeleton, dropdown), darkmode
### Executed commands
```bash
npx create-next-app ${projectName}
cd ${projectName}
npx strapup run-script Add-ShadcnUI-TS
```mdx title="./docs/Included scripts/Create-Next-ShadcnUI-TS.mdx"
---
title: Create-Next-ShadcnUI-TS
sortNum: 1000
---
### Create-Next-ShadcnUI-TS
New Next.js app, Shadcn UI add (button, input, skeleton, dropdown), darkmode
### Executed commands
```bash
npx create-next-app ${projectName}
cd ${projectName}
npx strapup run-script Add-ShadcnUI-TS
```mdx title="./docs/Included templates/contentlayer-basic-styled-next-mdx-components.mdx"
---
title: contentlayer-basic-styled-next-mdx-components
sortNum: 100
---
### contentlayer-basic-styled-next-mdx-components

#### Pasted files structure
```bash
└── components
    └── mdx-components.tsx1 directory, 1 file
```mdx title="./docs/Included templates/contentlayer-basic-styled-next-mdx-components.mdx"
---
title: contentlayer-basic-styled-next-mdx-components
sortNum: 100
---
### contentlayer-basic-styled-next-mdx-components

#### Pasted files structure
```bash
└── components
    └── mdx-components.tsx1 directory, 1 file

Files contents

./components/mdx-components.tsx
import * as React from "react"
import Image from "next/image"
import { useMDXComponent } from "next-contentlayer/hooks"
 
import { cn } from "@/lib/utils"
 
const components = {
    h1: ({ className, ...props }: React.ImgHTMLAttributes<HTMLHeadingElement>) => (
        <h1
            className={cn(
                "mt-2 scroll-m-20 text-4xl font-bold tracking-tight",
                className
            )}
            {...props}
        />
    ),
    h2: ({ className, ...props }: React.ImgHTMLAttributes<HTMLHeadingElement>) => (
        <h2
            className={cn(
                "mt-10 scroll-m-20 border-b pb-1 text-3xl font-semibold tracking-tight first:mt-0",
                className
            )}
            {...props}
        />
    ),
    h3: ({ className, ...props }: React.ImgHTMLAttributes<HTMLHeadingElement>) => (
        <h3
            className={cn(
                "mt-8 scroll-m-20 text-2xl font-semibold tracking-tight",
                className
            )}
            {...props}
        />
    ),
    h4: ({ className, ...props }: React.ImgHTMLAttributes<HTMLHeadingElement>) => (
        <h4
            className={cn(
                "mt-8 scroll-m-20 text-xl font-semibold tracking-tight",
                className
            )}
            {...props}
        />
    ),
    h5: ({ className, ...props }: React.ImgHTMLAttributes<HTMLHeadingElement>) => (
        <h5
            className={cn(
                "mt-8 scroll-m-20 text-lg font-semibold tracking-tight",
                className
            )}
            {...props}
        />
    ),
    h6: ({ className, ...props }: React.ImgHTMLAttributes<HTMLHeadingElement>) => (
        <h6
            className={cn(
                "mt-8 scroll-m-20 text-base font-semibold tracking-tight",
                className
            )}
            {...props}
        />
    ),
    a: ({ className, ...props }: React.ImgHTMLAttributes<HTMLAnchorElement>) => (
        <a
            className={cn("font-medium underline underline-offset-4", className)}
            {...props}
        />
    ),
    p: ({ className, ...props }: React.ImgHTMLAttributes<HTMLParagraphElement>) => (
        <p
            className={cn("leading-7 [&:not(:first-child)]:mt-6", className)}
            {...props}
        />
    ),
    ul: ({ className, ...props }: React.ImgHTMLAttributes<HTMLUListElement>) => (
        <ul className={cn("my-6 ml-6 list-disc", className)} {...props} />
    ),
    ol: ({ className, ...props }: React.ImgHTMLAttributes<HTMLOListElement>) => (
        <ol className={cn("my-6 ml-6 list-decimal", className)} {...props} />
    ),
    li: ({ className, ...props }: React.ImgHTMLAttributes<HTMLLIElement>) => (
        <li className={cn("mt-2", className)} {...props} />
    ),
    blockquote: ({ className, ...props }: React.ImgHTMLAttributes<HTMLQuoteElement>) => (
        <blockquote
            className={cn(
                "mt-6 border-l-2 pl-6 italic [&>*]:text-muted-foreground",
                className
            )}
            {...props}
        />
    ),
    img: ({
        className,
        alt,
        ...props
    }: React.ImgHTMLAttributes<HTMLImageElement>) => (
        // eslint-disable-next-line @next/next/no-img-element
        <img className={cn("rounded-md border", className)} alt={alt} {...props} />
    ),
    hr: ({ ...props }) => <hr className="my-4 md:my-8" {...props} />,
    table: ({ className, ...props }: React.HTMLAttributes<HTMLTableElement>) => (
        <div className="my-6 w-full overflow-y-auto">
            <table className={cn("w-full", className)} {...props} />
        </div>
    ),
    tr: ({ className, ...props }: React.HTMLAttributes<HTMLTableRowElement>) => (
        <tr
            className={cn("m-0 border-t p-0 even:bg-muted", className)}
            {...props}
        />
    ),
    th: ({ className, ...props }: React.ImgHTMLAttributes<HTMLTableCellElement>) => (
        <th
            className={cn(
                "border px-4 py-2 text-left font-bold [&[align=center]]:text-center [&[align=right]]:text-right",
                className
            )}
            {...props}
        />
    ),
    td: ({ className, ...props }: React.ImgHTMLAttributes<HTMLTableCellElement>) => (
        <td
            className={cn(
                "border px-4 py-2 text-left [&[align=center]]:text-center [&[align=right]]:text-right",
                className
            )}
            {...props}
        />
    ),
    pre: ({ className, ...props }: React.ImgHTMLAttributes<HTMLPreElement>) => (
        <pre
            className={cn(
                "mb-4 mt-6 overflow-x-auto rounded-lg border bg-black py-4",
                className
            )}
            {...props}
        />
    ),
    code: ({ className, ...props }: React.ImgHTMLAttributes<HTMLElement>) => (
        <code
            className={cn(
                "relative rounded border px-[0.3rem] py-[0.2rem] font-mono text-sm",
                className
            )}
            {...props}
        />
    ),
    Image,
}
 
interface MdxProps {
    code: string
}
 
export function Mdx({ code }: MdxProps) {
    const Component = useMDXComponent(code)
 
    return (
        <div className="mdx">
            <Component components={components} />
        </div>
    )
}
./components/mdx-components.tsx
import * as React from "react"
import Image from "next/image"
import { useMDXComponent } from "next-contentlayer/hooks"
 
import { cn } from "@/lib/utils"
 
const components = {
    h1: ({ className, ...props }: React.ImgHTMLAttributes<HTMLHeadingElement>) => (
        <h1
            className={cn(
                "mt-2 scroll-m-20 text-4xl font-bold tracking-tight",
                className
            )}
            {...props}
        />
    ),
    h2: ({ className, ...props }: React.ImgHTMLAttributes<HTMLHeadingElement>) => (
        <h2
            className={cn(
                "mt-10 scroll-m-20 border-b pb-1 text-3xl font-semibold tracking-tight first:mt-0",
                className
            )}
            {...props}
        />
    ),
    h3: ({ className, ...props }: React.ImgHTMLAttributes<HTMLHeadingElement>) => (
        <h3
            className={cn(
                "mt-8 scroll-m-20 text-2xl font-semibold tracking-tight",
                className
            )}
            {...props}
        />
    ),
    h4: ({ className, ...props }: React.ImgHTMLAttributes<HTMLHeadingElement>) => (
        <h4
            className={cn(
                "mt-8 scroll-m-20 text-xl font-semibold tracking-tight",
                className
            )}
            {...props}
        />
    ),
    h5: ({ className, ...props }: React.ImgHTMLAttributes<HTMLHeadingElement>) => (
        <h5
            className={cn(
                "mt-8 scroll-m-20 text-lg font-semibold tracking-tight",
                className
            )}
            {...props}
        />
    ),
    h6: ({ className, ...props }: React.ImgHTMLAttributes<HTMLHeadingElement>) => (
        <h6
            className={cn(
                "mt-8 scroll-m-20 text-base font-semibold tracking-tight",
                className
            )}
            {...props}
        />
    ),
    a: ({ className, ...props }: React.ImgHTMLAttributes<HTMLAnchorElement>) => (
        <a
            className={cn("font-medium underline underline-offset-4", className)}
            {...props}
        />
    ),
    p: ({ className, ...props }: React.ImgHTMLAttributes<HTMLParagraphElement>) => (
        <p
            className={cn("leading-7 [&:not(:first-child)]:mt-6", className)}
            {...props}
        />
    ),
    ul: ({ className, ...props }: React.ImgHTMLAttributes<HTMLUListElement>) => (
        <ul className={cn("my-6 ml-6 list-disc", className)} {...props} />
    ),
    ol: ({ className, ...props }: React.ImgHTMLAttributes<HTMLOListElement>) => (
        <ol className={cn("my-6 ml-6 list-decimal", className)} {...props} />
    ),
    li: ({ className, ...props }: React.ImgHTMLAttributes<HTMLLIElement>) => (
        <li className={cn("mt-2", className)} {...props} />
    ),
    blockquote: ({ className, ...props }: React.ImgHTMLAttributes<HTMLQuoteElement>) => (
        <blockquote
            className={cn(
                "mt-6 border-l-2 pl-6 italic [&>*]:text-muted-foreground",
                className
            )}
            {...props}
        />
    ),
    img: ({
        className,
        alt,
        ...props
    }: React.ImgHTMLAttributes<HTMLImageElement>) => (
        // eslint-disable-next-line @next/next/no-img-element
        <img className={cn("rounded-md border", className)} alt={alt} {...props} />
    ),
    hr: ({ ...props }) => <hr className="my-4 md:my-8" {...props} />,
    table: ({ className, ...props }: React.HTMLAttributes<HTMLTableElement>) => (
        <div className="my-6 w-full overflow-y-auto">
            <table className={cn("w-full", className)} {...props} />
        </div>
    ),
    tr: ({ className, ...props }: React.HTMLAttributes<HTMLTableRowElement>) => (
        <tr
            className={cn("m-0 border-t p-0 even:bg-muted", className)}
            {...props}
        />
    ),
    th: ({ className, ...props }: React.ImgHTMLAttributes<HTMLTableCellElement>) => (
        <th
            className={cn(
                "border px-4 py-2 text-left font-bold [&[align=center]]:text-center [&[align=right]]:text-right",
                className
            )}
            {...props}
        />
    ),
    td: ({ className, ...props }: React.ImgHTMLAttributes<HTMLTableCellElement>) => (
        <td
            className={cn(
                "border px-4 py-2 text-left [&[align=center]]:text-center [&[align=right]]:text-right",
                className
            )}
            {...props}
        />
    ),
    pre: ({ className, ...props }: React.ImgHTMLAttributes<HTMLPreElement>) => (
        <pre
            className={cn(
                "mb-4 mt-6 overflow-x-auto rounded-lg border bg-black py-4",
                className
            )}
            {...props}
        />
    ),
    code: ({ className, ...props }: React.ImgHTMLAttributes<HTMLElement>) => (
        <code
            className={cn(
                "relative rounded border px-[0.3rem] py-[0.2rem] font-mono text-sm",
                className
            )}
            {...props}
        />
    ),
    Image,
}
 
interface MdxProps {
    code: string
}
 
export function Mdx({ code }: MdxProps) {
    const Component = useMDXComponent(code)
 
    return (
        <div className="mdx">
            <Component components={components} />
        </div>
    )
}
```mdx title="./docs/Included templates/contentlayer-config.mdx"
---
title: contentlayer-config
sortNum: 101
---
### contentlayer-config

#### Pasted files structure
```bash
└── contentlayer.config.ts
```mdx title="./docs/Included templates/contentlayer-config.mdx"
---
title: contentlayer-config
sortNum: 101
---
### contentlayer-config

#### Pasted files structure
```bash
└── contentlayer.config.ts

Files contents

./contentlayer.config.ts
import { defineDocumentType, makeSource } from 'contentlayer/source-files'
import remarkGfm from 'remark-gfm'
import rehypeSlug from 'rehype-slug'
import rehypeAutolinkHeadings from 'rehype-autolink-headings'
import rehypePreetyCode from 'rehype-pretty-code'
 
export const Doc = defineDocumentType(() => ({
    name: 'Doc',
    filePathPattern: `**/*.mdx`,
    fields: {
        title: { type: 'string', required: true },
        sortNum: { type: 'number', required: true },
    },
    computedFields: {
        slug: { type: 'string', resolve: (doc) => `/${doc._raw.flattenedPath}` },
        slugAsParams: { type: 'string', resolve: (doc) => doc._raw.flattenedPath.split('/').slice(1).join('/') },
    },
    contentType: 'mdx'
}))
 
export default makeSource({
    contentDirPath: 'docs',
    documentTypes: [Doc],
    mdx: {
        remarkPlugins: [remarkGfm],
        rehypePlugins: [
            rehypeSlug,
            [
                rehypePreetyCode,
                {
                    theme: {
                        light: 'github-light',
                        dark: 'github-dark'
                    },
                    keepBackground: false,
                    onVisitLine: (node: any) => {
                        if (node.children.length === 0) {
                            node.children = [{ type: 'text', value: ' ' }]
                        }
                    },
                    onVisitHighlightedLine: (node: any) => {
                        node.properties.className.push('line--highlighted')
                    },
                    onVisitHighlitedWord: (node: any) => {
                        node.properties.className = ['word--highlighted']
                    }
                }
            ],
            [
                rehypeAutolinkHeadings,
                {
                    behavior: 'wrap',
                    properties: {
                        className: ['subheading--anchor'],
                        ariaLabel: 'Link to section'
                    }
                }
            ]
        ]
    }
})
./contentlayer.config.ts
import { defineDocumentType, makeSource } from 'contentlayer/source-files'
import remarkGfm from 'remark-gfm'
import rehypeSlug from 'rehype-slug'
import rehypeAutolinkHeadings from 'rehype-autolink-headings'
import rehypePreetyCode from 'rehype-pretty-code'
 
export const Doc = defineDocumentType(() => ({
    name: 'Doc',
    filePathPattern: `**/*.mdx`,
    fields: {
        title: { type: 'string', required: true },
        sortNum: { type: 'number', required: true },
    },
    computedFields: {
        slug: { type: 'string', resolve: (doc) => `/${doc._raw.flattenedPath}` },
        slugAsParams: { type: 'string', resolve: (doc) => doc._raw.flattenedPath.split('/').slice(1).join('/') },
    },
    contentType: 'mdx'
}))
 
export default makeSource({
    contentDirPath: 'docs',
    documentTypes: [Doc],
    mdx: {
        remarkPlugins: [remarkGfm],
        rehypePlugins: [
            rehypeSlug,
            [
                rehypePreetyCode,
                {
                    theme: {
                        light: 'github-light',
                        dark: 'github-dark'
                    },
                    keepBackground: false,
                    onVisitLine: (node: any) => {
                        if (node.children.length === 0) {
                            node.children = [{ type: 'text', value: ' ' }]
                        }
                    },
                    onVisitHighlightedLine: (node: any) => {
                        node.properties.className.push('line--highlighted')
                    },
                    onVisitHighlitedWord: (node: any) => {
                        node.properties.className = ['word--highlighted']
                    }
                }
            ],
            [
                rehypeAutolinkHeadings,
                {
                    behavior: 'wrap',
                    properties: {
                        className: ['subheading--anchor'],
                        ariaLabel: 'Link to section'
                    }
                }
            ]
        ]
    }
})
```mdx title="./docs/Included templates/contentlayer-next-docs-page-with-nav.mdx"
---
title: contentlayer-next-docs-page-with-nav
sortNum: 102
---
### contentlayer-next-docs-page-with-nav
Nav between documents and on page heading elements
#### Pasted files structure
```bash
├── app
│   └── docs
│       └── [[...slug]]
│           ├── layout.tsx
│           └── page.tsx
└── components
    └── DocsSideNav.tsx
```mdx title="./docs/Included templates/contentlayer-next-docs-page-with-nav.mdx"
---
title: contentlayer-next-docs-page-with-nav
sortNum: 102
---
### contentlayer-next-docs-page-with-nav
Nav between documents and on page heading elements
#### Pasted files structure
```bash
├── app
│   └── docs
│       └── [[...slug]]
│           ├── layout.tsx
│           └── page.tsx
└── components
    └── DocsSideNav.tsx

Files contents

./app/docs/[[...slug]]/layout.tsx
import DocsSideNav from "@/components/DocsSideNav";
import PageContentNav from "@/components/PageContentNav";
import '../../../styles/docs.css'
 
export default function DocsLayout({ children }: { children: React.ReactNode }) {
    return (
        <div className="grid grid-cols-[1fr_3fr_1fr] max-w-screen-xl mx-auto mt-10 relative mb-32">
            <DocsSideNav />
            {children}
            <PageContentNav />
        </div>
    )
}
./app/docs/[[...slug]]/layout.tsx
import DocsSideNav from "@/components/DocsSideNav";
import PageContentNav from "@/components/PageContentNav";
import '../../../styles/docs.css'
 
export default function DocsLayout({ children }: { children: React.ReactNode }) {
    return (
        <div className="grid grid-cols-[1fr_3fr_1fr] max-w-screen-xl mx-auto mt-10 relative mb-32">
            <DocsSideNav />
            {children}
            <PageContentNav />
        </div>
    )
}
./app/docs/[[...slug]]/page.tsx
import { allDocs } from "@/.contentlayer/generated"
import { Mdx } from "@/components/mdx-components"
import { redirect } from 'next/navigation'
import { MdKeyboardArrowRight } from 'react-icons/md'
 
const getDocBySlugs = (slugs: string[]) => {
    const pathFromSlugs = slugs.join('/')
    const doc = allDocs.find((doc) => doc._raw.flattenedPath === pathFromSlugs)
    if (!doc) return redirect('/docs/Basic use/Save and Paste')
    return doc
}
 
export default function DocsPage({ params }: { params: { slug?: string[] } }) {
 
    if (!params.slug) return redirect('/docs/Basic use/Save and Paste')
    params.slug = params.slug?.map((slug) => decodeURI(slug))
 
    const doc = getDocBySlugs(params.slug)
 
    return (
        <article className="min-h-screen pb-8 overflow-x-auto px-8 border-l">
            <div className="flex mb-4 gap-1 items-center text-sm">
                <p className="text-muted-foreground">{doc._raw.sourceFileDir}</p>
                <MdKeyboardArrowRight className="text-muted-foreground" />
                <p className="font-medium text-indigo-800 dark:bg-zinc-900 dark:text-indigo-600 bg-indigo-100 px-2 rounded-full">{doc.title}</p>
            </div>
            <Mdx code={doc?.body.code!} />
        </article>
    )
}
./app/docs/[[...slug]]/page.tsx
import { allDocs } from "@/.contentlayer/generated"
import { Mdx } from "@/components/mdx-components"
import { redirect } from 'next/navigation'
import { MdKeyboardArrowRight } from 'react-icons/md'
 
const getDocBySlugs = (slugs: string[]) => {
    const pathFromSlugs = slugs.join('/')
    const doc = allDocs.find((doc) => doc._raw.flattenedPath === pathFromSlugs)
    if (!doc) return redirect('/docs/Basic use/Save and Paste')
    return doc
}
 
export default function DocsPage({ params }: { params: { slug?: string[] } }) {
 
    if (!params.slug) return redirect('/docs/Basic use/Save and Paste')
    params.slug = params.slug?.map((slug) => decodeURI(slug))
 
    const doc = getDocBySlugs(params.slug)
 
    return (
        <article className="min-h-screen pb-8 overflow-x-auto px-8 border-l">
            <div className="flex mb-4 gap-1 items-center text-sm">
                <p className="text-muted-foreground">{doc._raw.sourceFileDir}</p>
                <MdKeyboardArrowRight className="text-muted-foreground" />
                <p className="font-medium text-indigo-800 dark:bg-zinc-900 dark:text-indigo-600 bg-indigo-100 px-2 rounded-full">{doc.title}</p>
            </div>
            <Mdx code={doc?.body.code!} />
        </article>
    )
}
./components/DocsSideNav.tsx
'use client'
 
import { usePathname } from 'next/navigation'
import { Doc, allDocs } from "@/.contentlayer/generated";
import Link from "next/link";
import { cn } from '@/lib/utils';
 
 
export default function DocsSideNav() {
    const sortedArr = allDocs.sort((a, b) => a.sortNum - b.sortNum)
    const path = decodeURI(usePathname())
 
    const categorizedDocs = sortedArr.reduce((acc: { [key: Doc['_raw']['sourceFileDir']]: Doc[] }, doc) => {
        const currCatContent = acc[doc._raw.sourceFileDir]
        return {
            ...acc,
            [doc._raw.sourceFileDir]: [...(currCatContent ? currCatContent : []), doc]
        }
    }, {})
 
    return (
        <div className="relative pr-6">
            <div className='sticky top-[7.04rem] text-sm'>
                {Object.entries(categorizedDocs).map(([cat, docs], i) => {
                    return (
                        <div key={i} className="mb-4">
                            <p className="text-foreground font-semibold mb-2">{cat}</p>
                            <div className="flex flex-col gap-1">
                                {docs.map((doc, j) => {
                                    const isActive = path === `/docs/${doc._raw.flattenedPath}`
 
                                    return <Link key={j} style={{ contain: "inline-size" }} href={`/docs/${doc._raw.flattenedPath}`} className={cn("ml-0 text-muted-foreground hover:text-indigo-700 dark:hover:text-indigo-600 transition-all hover:bg-indigo-50 py-1 px-2 rounded-md text-ellipsis whitespace-nowrap overflow-hidden dark:hover:bg-zinc-900", isActive ? "bg-indigo-50 dark:bg-zinc-900 dark:text-indigo-600 text-indigo-700" : "")}>{doc.title}</Link>
                                })}
                            </div>
                        </div>
                    )
                })}
            </div>
        </div>
    )
}
./components/DocsSideNav.tsx
'use client'
 
import { usePathname } from 'next/navigation'
import { Doc, allDocs } from "@/.contentlayer/generated";
import Link from "next/link";
import { cn } from '@/lib/utils';
 
 
export default function DocsSideNav() {
    const sortedArr = allDocs.sort((a, b) => a.sortNum - b.sortNum)
    const path = decodeURI(usePathname())
 
    const categorizedDocs = sortedArr.reduce((acc: { [key: Doc['_raw']['sourceFileDir']]: Doc[] }, doc) => {
        const currCatContent = acc[doc._raw.sourceFileDir]
        return {
            ...acc,
            [doc._raw.sourceFileDir]: [...(currCatContent ? currCatContent : []), doc]
        }
    }, {})
 
    return (
        <div className="relative pr-6">
            <div className='sticky top-[7.04rem] text-sm'>
                {Object.entries(categorizedDocs).map(([cat, docs], i) => {
                    return (
                        <div key={i} className="mb-4">
                            <p className="text-foreground font-semibold mb-2">{cat}</p>
                            <div className="flex flex-col gap-1">
                                {docs.map((doc, j) => {
                                    const isActive = path === `/docs/${doc._raw.flattenedPath}`
 
                                    return <Link key={j} style={{ contain: "inline-size" }} href={`/docs/${doc._raw.flattenedPath}`} className={cn("ml-0 text-muted-foreground hover:text-indigo-700 dark:hover:text-indigo-600 transition-all hover:bg-indigo-50 py-1 px-2 rounded-md text-ellipsis whitespace-nowrap overflow-hidden dark:hover:bg-zinc-900", isActive ? "bg-indigo-50 dark:bg-zinc-900 dark:text-indigo-600 text-indigo-700" : "")}>{doc.title}</Link>
                                })}
                            </div>
                        </div>
                    )
                })}
            </div>
        </div>
    )
}
```mdx title="./docs/Included templates/contentlayer-next-styled-mdx-components.mdx"
---
title: contentlayer-next-styled-mdx-components
sortNum: 103
---
### contentlayer-next-styled-mdx-components

#### Pasted files structure
```bash
└── components
    └── mdx-components.tsx1 directory, 1 file
```mdx title="./docs/Included templates/contentlayer-next-styled-mdx-components.mdx"
---
title: contentlayer-next-styled-mdx-components
sortNum: 103
---
### contentlayer-next-styled-mdx-components

#### Pasted files structure
```bash
└── components
    └── mdx-components.tsx1 directory, 1 file

Files contents

./components/mdx-components.tsx
'use client'
 
import { cn } from "@/lib/utils"
import { useMDXComponent } from "next-contentlayer/hooks"
import Image from "next/image"
import Link from "next/link"
import * as React from "react"
import toast from "react-hot-toast"
import { BsLink45Deg } from 'react-icons/bs'
import { FiBookOpen, FiCopy } from "react-icons/fi"
import { IoArrowForwardOutline } from 'react-icons/io5'
 
interface NextCardHolderProps {
    cards: {
        title: string
        description: string
        link: string
        iconType?: 'arrow' | 'book'
    }[]
}
 
function NextCardHolder({ cards }: NextCardHolderProps) {
    return (
        <div id="grid-card" className="grid grid-cols-2 gap-4 mt-6">
            {cards.map(({ description, iconType = 'arrow', link, title }, index) => (
                <Link href={link} key={index} className="flex flex-col border px-4 py-3 rounded-lg shadow-lg cursor-pointer">
                    <div className="flex flex-row-reverse justify-between gap-2">
                        <h3 className="text-lg font-semibold text-left">{title}</h3>
                        {iconType === 'arrow' && <IoArrowForwardOutline className="text-3xl" />}
                        {iconType === 'book' && <FiBookOpen className="text-3xl" />}
                    </div>
                    <p className="text-base text-left">{description}</p>
                </Link>
            ))}
        </div>
    )
}
 
const components = {
    h1: ({ className, ...props }: React.HTMLAttributes<HTMLHeadingElement>) => (
        <h1
            className={cn(
                "mt-2 scroll-m-20 text-4xl font-bold tracking-tight",
                className
            )}
            {...props}
        />
    ),
    h2: ({ className, ...props }: React.HTMLAttributes<HTMLHeadingElement>) => (
        <h2
            className={cn(
                "mt-10 scroll-m-20 border-b pb-1 text-3xl font-semibold tracking-tight first:mt-0",
                className
            )}
            {...props}
        />
    ),
    h3: ({ className, ...props }: React.HTMLAttributes<HTMLHeadingElement>) => (
        <h3
            className={cn(
                "mt-8 scroll-m-20 text-2xl font-semibold tracking-tight",
                className
            )}
            {...props}
        />
    ),
    h4: ({ className, ...props }: React.HTMLAttributes<HTMLHeadingElement>) => (
        <h4
            className={cn(
                "mt-8 scroll-m-20 text-xl font-semibold tracking-tight",
                className
            )}
            {...props}
        />
    ),
    h5: ({ className, ...props }: React.HTMLAttributes<HTMLHeadingElement>) => (
        <h5
            className={cn(
                "mt-8 scroll-m-20 text-lg font-semibold tracking-tight",
                className
            )}
            {...props}
        />
    ),
    h6: ({ className, ...props }: React.HTMLAttributes<HTMLHeadingElement>) => (
        <h6
            className={cn(
                "mt-8 scroll-m-20 text-base font-semibold tracking-tight",
                className
            )}
            {...props}
        />
    ),
    a: ({ className, ...props }: React.HTMLAttributes<HTMLAnchorElement>) => {
        if (props['aria-label'] === "Link to section") {
            return <a
                className={cn("group flex items-center gap-1 relative", className)}
                {...props}
            >
                <BsLink45Deg className="absolute -left-6 group-hover:visible invisible" />
                {props.children}
            </a>
        }
 
        return <a
            className={cn("font-medium underline underline-offset-4", className)}
            {...props}
        />
    },
    p: ({ className, ...props }: React.HTMLAttributes<HTMLParagraphElement>) => (
        <p
            className={cn("leading-7 [&:not(:first-child)]:mt-6", className)}
            {...props}
        />
    ),
    ul: ({ className, ...props }: React.HTMLAttributes<HTMLUListElement>) => (
        <ul className={cn("my-6 ml-6 list-disc", className)} {...props} />
    ),
    ol: ({ className, ...props }: React.HTMLAttributes<HTMLOListElement>) => (
        <ol className={cn("my-6 ml-6 list-decimal", className)} {...props} />
    ),
    li: ({ className, ...props }: React.HTMLAttributes<HTMLLIElement>) => (
        <li className={cn("mt-2", className)} {...props} />
    ),
    blockquote: ({ className, ...props }: React.HTMLAttributes<HTMLQuoteElement>) => (
        <blockquote
            className={cn(
                "mt-6 border-l-2 pl-6 italic [&>*]:text-muted-foreground",
                className
            )}
            {...props}
        />
    ),
    img: ({
        className,
        alt,
        ...props
    }: React.ImgHTMLAttributes<HTMLImageElement>) => (
        // eslint-disable-next-line @next/next/no-img-element
        <img className={cn("rounded-md border mt-4", className)} alt={alt} {...props} />
    ),
    hr: ({ ...props }) => <hr className="my-4 md:my-8" {...props} />,
    table: ({ className, ...props }: React.HTMLAttributes<HTMLTableElement>) => (
        <div className="my-6 w-full overflow-y-auto">
            <table className={cn("w-full", className)} {...props} />
        </div>
    ),
    tr: ({ className, ...props }: React.HTMLAttributes<HTMLTableRowElement>) => (
        <tr
            className={cn("m-0 border-t p-0 even:bg-muted", className)}
            {...props}
        />
    ),
    th: ({ className, ...props }: React.HTMLAttributes<HTMLTableCellElement>) => (
        <th
            className={cn(
                "border px-4 py-2 text-left font-bold [&[align=center]]:text-center [&[align=right]]:text-right",
                className
            )}
            {...props}
        />
    ),
    td: ({ className, ...props }: React.HTMLAttributes<HTMLTableCellElement>) => (
        <td
            className={cn(
                "border px-4 py-2 text-left [&[align=center]]:text-center [&[align=right]]:text-right",
                className
            )}
            {...props}
        />
    ),
    pre: ({ className, ...props }: React.HTMLAttributes<HTMLPreElement>) => {
        // @ts-ignore
        if (props['data-language'] === 'command') return <pre
            className={cn(
                "mb-4 mt-6 overflow-x-auto rounded-lg bg-indigo-50 dark:bg-zinc-900 dark:text-zinc-50",
                className
            )}
            {...props}
        />
 
        return <pre
            className={cn(
                "mb-4 mt-6 overflow-x-auto rounded-lg bg-indigo-50 py-4 dark:bg-zinc-900 dark:text-zinc-50",
                className
            )}
            {...props}
        />
    },
    code: ({ className, ...props }: React.HTMLAttributes<HTMLElement>) => {
        // @ts-ignore
        if (props['data-language'] === 'command') return <code
            className={cn(
                "relative rounded px-[1rem] py-[0.5rem] font-mono bg-indigo-50 dark:bg-zinc-900",
                className
            )}
            {...props}
        >
            <FiCopy onClick={() => toast.success('copied! 📝')} className="absolute top-1/2 -translate-y-1/2 right-1 hover:bg-indigo-200 p-2 rounded-md box-content transition-all cursor-pointer" />
            {props.children}
        </code>
 
        return <code
            className={cn(
                "relative rounded px-[0.3rem] py-[0.2rem] font-mono text-sm bg-indigo-50 dark:bg-zinc-900",
                className
            )}
            {...props}
        />
    },
    Image,
    NextCardHolder
}
 
interface MdxProps {
    code: string
}
 
export function Mdx({ code }: MdxProps) {
    const Component = useMDXComponent(code)
 
    return (
        <div className="mdx">
            <Component components={components} />
        </div>
    )
}
./components/mdx-components.tsx
'use client'
 
import { cn } from "@/lib/utils"
import { useMDXComponent } from "next-contentlayer/hooks"
import Image from "next/image"
import Link from "next/link"
import * as React from "react"
import toast from "react-hot-toast"
import { BsLink45Deg } from 'react-icons/bs'
import { FiBookOpen, FiCopy } from "react-icons/fi"
import { IoArrowForwardOutline } from 'react-icons/io5'
 
interface NextCardHolderProps {
    cards: {
        title: string
        description: string
        link: string
        iconType?: 'arrow' | 'book'
    }[]
}
 
function NextCardHolder({ cards }: NextCardHolderProps) {
    return (
        <div id="grid-card" className="grid grid-cols-2 gap-4 mt-6">
            {cards.map(({ description, iconType = 'arrow', link, title }, index) => (
                <Link href={link} key={index} className="flex flex-col border px-4 py-3 rounded-lg shadow-lg cursor-pointer">
                    <div className="flex flex-row-reverse justify-between gap-2">
                        <h3 className="text-lg font-semibold text-left">{title}</h3>
                        {iconType === 'arrow' && <IoArrowForwardOutline className="text-3xl" />}
                        {iconType === 'book' && <FiBookOpen className="text-3xl" />}
                    </div>
                    <p className="text-base text-left">{description}</p>
                </Link>
            ))}
        </div>
    )
}
 
const components = {
    h1: ({ className, ...props }: React.HTMLAttributes<HTMLHeadingElement>) => (
        <h1
            className={cn(
                "mt-2 scroll-m-20 text-4xl font-bold tracking-tight",
                className
            )}
            {...props}
        />
    ),
    h2: ({ className, ...props }: React.HTMLAttributes<HTMLHeadingElement>) => (
        <h2
            className={cn(
                "mt-10 scroll-m-20 border-b pb-1 text-3xl font-semibold tracking-tight first:mt-0",
                className
            )}
            {...props}
        />
    ),
    h3: ({ className, ...props }: React.HTMLAttributes<HTMLHeadingElement>) => (
        <h3
            className={cn(
                "mt-8 scroll-m-20 text-2xl font-semibold tracking-tight",
                className
            )}
            {...props}
        />
    ),
    h4: ({ className, ...props }: React.HTMLAttributes<HTMLHeadingElement>) => (
        <h4
            className={cn(
                "mt-8 scroll-m-20 text-xl font-semibold tracking-tight",
                className
            )}
            {...props}
        />
    ),
    h5: ({ className, ...props }: React.HTMLAttributes<HTMLHeadingElement>) => (
        <h5
            className={cn(
                "mt-8 scroll-m-20 text-lg font-semibold tracking-tight",
                className
            )}
            {...props}
        />
    ),
    h6: ({ className, ...props }: React.HTMLAttributes<HTMLHeadingElement>) => (
        <h6
            className={cn(
                "mt-8 scroll-m-20 text-base font-semibold tracking-tight",
                className
            )}
            {...props}
        />
    ),
    a: ({ className, ...props }: React.HTMLAttributes<HTMLAnchorElement>) => {
        if (props['aria-label'] === "Link to section") {
            return <a
                className={cn("group flex items-center gap-1 relative", className)}
                {...props}
            >
                <BsLink45Deg className="absolute -left-6 group-hover:visible invisible" />
                {props.children}
            </a>
        }
 
        return <a
            className={cn("font-medium underline underline-offset-4", className)}
            {...props}
        />
    },
    p: ({ className, ...props }: React.HTMLAttributes<HTMLParagraphElement>) => (
        <p
            className={cn("leading-7 [&:not(:first-child)]:mt-6", className)}
            {...props}
        />
    ),
    ul: ({ className, ...props }: React.HTMLAttributes<HTMLUListElement>) => (
        <ul className={cn("my-6 ml-6 list-disc", className)} {...props} />
    ),
    ol: ({ className, ...props }: React.HTMLAttributes<HTMLOListElement>) => (
        <ol className={cn("my-6 ml-6 list-decimal", className)} {...props} />
    ),
    li: ({ className, ...props }: React.HTMLAttributes<HTMLLIElement>) => (
        <li className={cn("mt-2", className)} {...props} />
    ),
    blockquote: ({ className, ...props }: React.HTMLAttributes<HTMLQuoteElement>) => (
        <blockquote
            className={cn(
                "mt-6 border-l-2 pl-6 italic [&>*]:text-muted-foreground",
                className
            )}
            {...props}
        />
    ),
    img: ({
        className,
        alt,
        ...props
    }: React.ImgHTMLAttributes<HTMLImageElement>) => (
        // eslint-disable-next-line @next/next/no-img-element
        <img className={cn("rounded-md border mt-4", className)} alt={alt} {...props} />
    ),
    hr: ({ ...props }) => <hr className="my-4 md:my-8" {...props} />,
    table: ({ className, ...props }: React.HTMLAttributes<HTMLTableElement>) => (
        <div className="my-6 w-full overflow-y-auto">
            <table className={cn("w-full", className)} {...props} />
        </div>
    ),
    tr: ({ className, ...props }: React.HTMLAttributes<HTMLTableRowElement>) => (
        <tr
            className={cn("m-0 border-t p-0 even:bg-muted", className)}
            {...props}
        />
    ),
    th: ({ className, ...props }: React.HTMLAttributes<HTMLTableCellElement>) => (
        <th
            className={cn(
                "border px-4 py-2 text-left font-bold [&[align=center]]:text-center [&[align=right]]:text-right",
                className
            )}
            {...props}
        />
    ),
    td: ({ className, ...props }: React.HTMLAttributes<HTMLTableCellElement>) => (
        <td
            className={cn(
                "border px-4 py-2 text-left [&[align=center]]:text-center [&[align=right]]:text-right",
                className
            )}
            {...props}
        />
    ),
    pre: ({ className, ...props }: React.HTMLAttributes<HTMLPreElement>) => {
        // @ts-ignore
        if (props['data-language'] === 'command') return <pre
            className={cn(
                "mb-4 mt-6 overflow-x-auto rounded-lg bg-indigo-50 dark:bg-zinc-900 dark:text-zinc-50",
                className
            )}
            {...props}
        />
 
        return <pre
            className={cn(
                "mb-4 mt-6 overflow-x-auto rounded-lg bg-indigo-50 py-4 dark:bg-zinc-900 dark:text-zinc-50",
                className
            )}
            {...props}
        />
    },
    code: ({ className, ...props }: React.HTMLAttributes<HTMLElement>) => {
        // @ts-ignore
        if (props['data-language'] === 'command') return <code
            className={cn(
                "relative rounded px-[1rem] py-[0.5rem] font-mono bg-indigo-50 dark:bg-zinc-900",
                className
            )}
            {...props}
        >
            <FiCopy onClick={() => toast.success('copied! 📝')} className="absolute top-1/2 -translate-y-1/2 right-1 hover:bg-indigo-200 p-2 rounded-md box-content transition-all cursor-pointer" />
            {props.children}
        </code>
 
        return <code
            className={cn(
                "relative rounded px-[0.3rem] py-[0.2rem] font-mono text-sm bg-indigo-50 dark:bg-zinc-900",
                className
            )}
            {...props}
        />
    },
    Image,
    NextCardHolder
}
 
interface MdxProps {
    code: string
}
 
export function Mdx({ code }: MdxProps) {
    const Component = useMDXComponent(code)
 
    return (
        <div className="mdx">
            <Component components={components} />
        </div>
    )
}
```mdx title="./docs/Included templates/next-redux-ts.mdx"
---
title: next-redux-ts
sortNum: 104
---
### next-redux-ts
Typescript redux toolkit with skeleton createApi, createSlice and TS redux hooks.
#### Pasted files structure
```bash
├── features
│   └── counterSlice.ts
├── hooks.ts
├── provider.tsx
├── services
│   └── userApi.ts
└── store.ts
```mdx title="./docs/Included templates/next-redux-ts.mdx"
---
title: next-redux-ts
sortNum: 104
---
### next-redux-ts
Typescript redux toolkit with skeleton createApi, createSlice and TS redux hooks.
#### Pasted files structure
```bash
├── features
│   └── counterSlice.ts
├── hooks.ts
├── provider.tsx
├── services
│   └── userApi.ts
└── store.ts

Files contents

./features/counterSlice.ts
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
 
type CounterState = {
    value: number;
};
 
const initialState = {
    value: 0,
} as CounterState;
 
export const counter = createSlice({
    name: "counter",
    initialState,
    reducers: {
        reset: () => initialState,
        increment: (state) => {
            state.value += 1;
        },
        incrementByAmount: (state, action: PayloadAction<number>) => {
            state.value += action.payload;
        },
    },
});
 
export const {
    increment,
    incrementByAmount,
} = counter.actions;
export default counter.reducer;
./features/counterSlice.ts
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
 
type CounterState = {
    value: number;
};
 
const initialState = {
    value: 0,
} as CounterState;
 
export const counter = createSlice({
    name: "counter",
    initialState,
    reducers: {
        reset: () => initialState,
        increment: (state) => {
            state.value += 1;
        },
        incrementByAmount: (state, action: PayloadAction<number>) => {
            state.value += action.payload;
        },
    },
});
 
export const {
    increment,
    incrementByAmount,
} = counter.actions;
export default counter.reducer;
./hooks.ts
import { TypedUseSelectorHook, useDispatch, useSelector } from "react-redux";
import type { RootState, AppDispatch } from "./store";
 
export const useAppDispatch = () => useDispatch<AppDispatch>();
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;
./hooks.ts
import { TypedUseSelectorHook, useDispatch, useSelector } from "react-redux";
import type { RootState, AppDispatch } from "./store";
 
export const useAppDispatch = () => useDispatch<AppDispatch>();
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;
./provider.tsx
"use client";
 
import { store } from "./store";
import { Provider } from "react-redux";
 
export function Providers({ children }: { children: React.ReactNode }) {
    return <Provider store={store}>{children}</Provider>;
}
./provider.tsx
"use client";
 
import { store } from "./store";
import { Provider } from "react-redux";
 
export function Providers({ children }: { children: React.ReactNode }) {
    return <Provider store={store}>{children}</Provider>;
}
./services/userApi.ts
import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react";
 
type User = {
    id: number;
    name: string;
    email: number;
};
 
export const userApi = createApi({
    reducerPath: "userApi",
    refetchOnFocus: true,
    baseQuery: fetchBaseQuery({
        baseUrl: "https://your-api-url.com/",
    }),
    endpoints: (builder) => ({
        getUsers: builder.query<User[], null>({
            query: () => "users",
        }),
        getUserById: builder.query<User, { id: string }>({
            query: ({ id }) => `users/${id}`,
        }),
    }),
});
 
export const { useGetUsersQuery, useGetUserByIdQuery } = userApi;
./services/userApi.ts
import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react";
 
type User = {
    id: number;
    name: string;
    email: number;
};
 
export const userApi = createApi({
    reducerPath: "userApi",
    refetchOnFocus: true,
    baseQuery: fetchBaseQuery({
        baseUrl: "https://your-api-url.com/",
    }),
    endpoints: (builder) => ({
        getUsers: builder.query<User[], null>({
            query: () => "users",
        }),
        getUserById: builder.query<User, { id: string }>({
            query: ({ id }) => `users/${id}`,
        }),
    }),
});
 
export const { useGetUsersQuery, useGetUserByIdQuery } = userApi;
./store.ts
import { configureStore } from "@reduxjs/toolkit";
import counterReducer from "./features/counterSlice";
import { userApi } from "./services/userApi";
import { setupListeners } from "@reduxjs/toolkit/dist/query";
 
export const store = configureStore({
    reducer: {
        counterReducer,
        [userApi.reducerPath]: userApi.reducer,
    },
    devTools: process.env.NODE_ENV !== "production",
    middleware: (getDefaultMiddleware) =>
        getDefaultMiddleware({}).concat([userApi.middleware]),
});
 
setupListeners(store.dispatch);
 
export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;
./store.ts
import { configureStore } from "@reduxjs/toolkit";
import counterReducer from "./features/counterSlice";
import { userApi } from "./services/userApi";
import { setupListeners } from "@reduxjs/toolkit/dist/query";
 
export const store = configureStore({
    reducer: {
        counterReducer,
        [userApi.reducerPath]: userApi.reducer,
    },
    devTools: process.env.NODE_ENV !== "production",
    middleware: (getDefaultMiddleware) =>
        getDefaultMiddleware({}).concat([userApi.middleware]),
});
 
setupListeners(store.dispatch);
 
export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;
```mdx title="./docs/Included templates/next-shad-darkmode.mdx"
---
title: next-shad-darkmode
sortNum: 105
---
### next-shad-darkmode

#### Pasted files structure
```bash
└── components
    ├── ThemeProvider.tsx
    └── ui
        └── ModeToggle.tsx
```mdx title="./docs/Included templates/next-shad-darkmode.mdx"
---
title: next-shad-darkmode
sortNum: 105
---
### next-shad-darkmode

#### Pasted files structure
```bash
└── components
    ├── ThemeProvider.tsx
    └── ui
        └── ModeToggle.tsx

Files contents

./components/ThemeProvider.tsx
'use client'
 
import { ThemeProvider as NextThemeProvider } from "next-themes"
 
export default function ThemeProvider({ children }: { children: React.ReactNode }) {
    return (
        <NextThemeProvider attribute="class" defaultTheme="light" enableSystem>
            {children}
        </NextThemeProvider>
    )
}
./components/ThemeProvider.tsx
'use client'
 
import { ThemeProvider as NextThemeProvider } from "next-themes"
 
export default function ThemeProvider({ children }: { children: React.ReactNode }) {
    return (
        <NextThemeProvider attribute="class" defaultTheme="light" enableSystem>
            {children}
        </NextThemeProvider>
    )
}
./components/ui/ModeToggle.tsx
"use client"
 
import * as React from "react"
import { Moon, Sun } from "lucide-react"
import { useTheme } from "next-themes"
 
import { Button } from "@/components/ui/button"
import {
    DropdownMenu,
    DropdownMenuContent,
    DropdownMenuItem,
    DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu"
 
export function ModeToggle() {
    const { setTheme } = useTheme()
 
    return (
        <DropdownMenu>
            <DropdownMenuTrigger asChild>
                <Button variant="outline" size="icon">
                    <Sun className="h-[1.2rem] w-[1.2rem] rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
                    <Moon className="absolute h-[1.2rem] w-[1.2rem] rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" />
                    <span className="sr-only">Toggle theme</span>
                </Button>
            </DropdownMenuTrigger>
            <DropdownMenuContent align="end">
                <DropdownMenuItem onClick={() => setTheme("light")}>
                    Light
                </DropdownMenuItem>
                <DropdownMenuItem onClick={() => setTheme("dark")}>
                    Dark
                </DropdownMenuItem>
                <DropdownMenuItem onClick={() => setTheme("system")}>
                    System
                </DropdownMenuItem>
            </DropdownMenuContent>
        </DropdownMenu>
    )
}
./components/ui/ModeToggle.tsx
"use client"
 
import * as React from "react"
import { Moon, Sun } from "lucide-react"
import { useTheme } from "next-themes"
 
import { Button } from "@/components/ui/button"
import {
    DropdownMenu,
    DropdownMenuContent,
    DropdownMenuItem,
    DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu"
 
export function ModeToggle() {
    const { setTheme } = useTheme()
 
    return (
        <DropdownMenu>
            <DropdownMenuTrigger asChild>
                <Button variant="outline" size="icon">
                    <Sun className="h-[1.2rem] w-[1.2rem] rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
                    <Moon className="absolute h-[1.2rem] w-[1.2rem] rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" />
                    <span className="sr-only">Toggle theme</span>
                </Button>
            </DropdownMenuTrigger>
            <DropdownMenuContent align="end">
                <DropdownMenuItem onClick={() => setTheme("light")}>
                    Light
                </DropdownMenuItem>
                <DropdownMenuItem onClick={() => setTheme("dark")}>
                    Dark
                </DropdownMenuItem>
                <DropdownMenuItem onClick={() => setTheme("system")}>
                    System
                </DropdownMenuItem>
            </DropdownMenuContent>
        </DropdownMenu>
    )
}
```mdx title="./docs/Included templates/next13-app-auth-paste-from-root.mdx"
---
title: next13-app-auth-paste-from-root
sortNum: 106
---
### next13-app-auth-paste-from-root
Adds basic auth.js with Github provider, React provider component and exmaple .env file to TS nextjs13 with app router. Must be paste in root folder, where app/ dir is located.
#### Pasted files structure
```bash
├── app
│   └── api
│       └── auth
│           └── [...nextauth]
│               └── route.ts
└── components
    └── AuthProvider.tsx
```mdx title="./docs/Included templates/next13-app-auth-paste-from-root.mdx"
---
title: next13-app-auth-paste-from-root
sortNum: 106
---
### next13-app-auth-paste-from-root
Adds basic auth.js with Github provider, React provider component and exmaple .env file to TS nextjs13 with app router. Must be paste in root folder, where app/ dir is located.
#### Pasted files structure
```bash
├── app
│   └── api
│       └── auth
│           └── [...nextauth]
│               └── route.ts
└── components
    └── AuthProvider.tsx

Files contents

./.env.auth-template
GITHUB_ID=9c7fd0feb8bb492b5082
GITHUB_SECRET=1086b7d5d18b020be74fd00a034a4d19be4380ae
NEXTAUTH_SECRET=LUIt4Y+LigEa/rXhJpbLqloJ2pbHUK271WUV5yLAZyI=
./.env.auth-template
GITHUB_ID=9c7fd0feb8bb492b5082
GITHUB_SECRET=1086b7d5d18b020be74fd00a034a4d19be4380ae
NEXTAUTH_SECRET=LUIt4Y+LigEa/rXhJpbLqloJ2pbHUK271WUV5yLAZyI=
./app/api/auth/[...nextauth]/route.ts
import NextAuth, { AuthOptions } from "next-auth"
import GithubProvider from "next-auth/providers/github"
 
export const authOptions: AuthOptions = {
    // Configure one or more authentication providers
    providers: [
        GithubProvider({
            clientId: process.env.GITHUB_ID!,
            clientSecret: process.env.GITHUB_SECRET!,
        }),
        // ...add more providers here
    ],
}
 
const handler = NextAuth(authOptions)
 
export { handler as GET, handler as POST }
./app/api/auth/[...nextauth]/route.ts
import NextAuth, { AuthOptions } from "next-auth"
import GithubProvider from "next-auth/providers/github"
 
export const authOptions: AuthOptions = {
    // Configure one or more authentication providers
    providers: [
        GithubProvider({
            clientId: process.env.GITHUB_ID!,
            clientSecret: process.env.GITHUB_SECRET!,
        }),
        // ...add more providers here
    ],
}
 
const handler = NextAuth(authOptions)
 
export { handler as GET, handler as POST }
./components/AuthProvider.tsx
'use client'
 
import { SessionProvider } from "next-auth/react";
 
export default function Providers({ children }: { children: React.ReactNode }) {
    return (
        <SessionProvider>
            {children}
        </SessionProvider>
    )
}
./components/AuthProvider.tsx
'use client'
 
import { SessionProvider } from "next-auth/react";
 
export default function Providers({ children }: { children: React.ReactNode }) {
    return (
        <SessionProvider>
            {children}
        </SessionProvider>
    )
}
```mdx title="./docs/Included templates/redux-ts.mdx"
---
title: "next-redux-ts"
sortNum: 4
---

### Redux Toolkit Typescript template for NextJS 13.
Consists of basic RTK setup with Typescript.
- Typesafe hooks
- Store setup
- Basic slice setup
- Basic API setup

#### Pasted files straucture
```bash
├── features
│   └── counterSlice.ts
├── services
│   └── userApi.ts
├── store.ts
├── hooks.ts
```mdx title="./docs/Included templates/redux-ts.mdx"
---
title: "next-redux-ts"
sortNum: 4
---

### Redux Toolkit Typescript template for NextJS 13.
Consists of basic RTK setup with Typescript.
- Typesafe hooks
- Store setup
- Basic slice setup
- Basic API setup

#### Pasted files straucture
```bash
├── features
│   └── counterSlice.ts
├── services
│   └── userApi.ts
├── store.ts
├── hooks.ts

Files contents

./features/counterSlice.ts
import { configureStore } from "@reduxjs/toolkit";
import counterReducer from "./features/counterSlice";
import { userApi } from "./services/userApi";
import { setupListeners } from "@reduxjs/toolkit/dist/query";
 
export const store = configureStore({
    reducer: {
        counterReducer,
        [userApi.reducerPath]: userApi.reducer,
    },
    devTools: process.env.NODE_ENV !== "production",
    middleware: (getDefaultMiddleware) =>
        getDefaultMiddleware({}).concat([userApi.middleware]),
});
 
setupListeners(store.dispatch);
 
export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;
./features/counterSlice.ts
import { configureStore } from "@reduxjs/toolkit";
import counterReducer from "./features/counterSlice";
import { userApi } from "./services/userApi";
import { setupListeners } from "@reduxjs/toolkit/dist/query";
 
export const store = configureStore({
    reducer: {
        counterReducer,
        [userApi.reducerPath]: userApi.reducer,
    },
    devTools: process.env.NODE_ENV !== "production",
    middleware: (getDefaultMiddleware) =>
        getDefaultMiddleware({}).concat([userApi.middleware]),
});
 
setupListeners(store.dispatch);
 
export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;
./services/userApi.ts
import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react";
 
type User = {
    id: number;
    name: string;
    email: number;
};
 
export const userApi = createApi({
    reducerPath: "userApi",
    refetchOnFocus: true,
    baseQuery: fetchBaseQuery({
        baseUrl: "https://your-api-url.com/",
    }),
    endpoints: (builder) => ({
        getUsers: builder.query<User[], null>({
            query: () => "users",
        }),
        getUserById: builder.query<User, { id: string }>({
            query: ({ id }) => `users/${id}`,
        }),
    }),
});
 
export const { useGetUsersQuery, useGetUserByIdQuery } = userApi;
./services/userApi.ts
import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react";
 
type User = {
    id: number;
    name: string;
    email: number;
};
 
export const userApi = createApi({
    reducerPath: "userApi",
    refetchOnFocus: true,
    baseQuery: fetchBaseQuery({
        baseUrl: "https://your-api-url.com/",
    }),
    endpoints: (builder) => ({
        getUsers: builder.query<User[], null>({
            query: () => "users",
        }),
        getUserById: builder.query<User, { id: string }>({
            query: ({ id }) => `users/${id}`,
        }),
    }),
});
 
export const { useGetUsersQuery, useGetUserByIdQuery } = userApi;
./store.ts
import { configureStore } from "@reduxjs/toolkit";
import counterReducer from "./features/counterSlice";
import { userApi } from "./services/userApi";
import { setupListeners } from "@reduxjs/toolkit/dist/query";
 
export const store = configureStore({
    reducer: {
        counterReducer,
        [userApi.reducerPath]: userApi.reducer,
    },
    devTools: process.env.NODE_ENV !== "production",
    middleware: (getDefaultMiddleware) =>
        getDefaultMiddleware({}).concat([userApi.middleware]),
});
 
setupListeners(store.dispatch);
 
export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;
./store.ts
import { configureStore } from "@reduxjs/toolkit";
import counterReducer from "./features/counterSlice";
import { userApi } from "./services/userApi";
import { setupListeners } from "@reduxjs/toolkit/dist/query";
 
export const store = configureStore({
    reducer: {
        counterReducer,
        [userApi.reducerPath]: userApi.reducer,
    },
    devTools: process.env.NODE_ENV !== "production",
    middleware: (getDefaultMiddleware) =>
        getDefaultMiddleware({}).concat([userApi.middleware]),
});
 
setupListeners(store.dispatch);
 
export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;
./hooks.tsx
import { TypedUseSelectorHook, useDispatch, useSelector } from "react-redux";
import type { RootState, AppDispatch } from "./store";
 
export const useAppDispatch = () => useDispatch<AppDispatch>();
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;
./hooks.tsx
import { TypedUseSelectorHook, useDispatch, useSelector } from "react-redux";
import type { RootState, AppDispatch } from "./store";
 
export const useAppDispatch = () => useDispatch<AppDispatch>();
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;
```css title="./styles/docs.css"
div[data-rehype-pretty-code-title] {
    margin-top: 0;
    background-color: #eef2ff;
    margin-top: 1.5rem;
    border-top-right-radius: 0.5rem;
    border-top-left-radius: 0.5rem;
    font-size: 0.875rem;
    padding: 0.5rem 0.5rem;
    border-bottom: 1px solid #dfe2f5;
    font-weight: 500;

    &+pre {
        margin-top: 0;
        padding-top: 0.5rem;
        border-top-left-radius: 0;
        border-top-right-radius: 0;
    }
}

.dark {
    & div[data-rehype-pretty-code-title] {
        margin-top: 0;
        background-color: #18181b;
        margin-top: 1.5rem;
        border-top-right-radius: 0.5rem;
        border-top-left-radius: 0.5rem;
        font-size: 0.875rem;
        padding: 0.5rem 0.5rem;
        border-bottom: 1px solid #27272a;
        font-weight: 500;

        &+pre {
            margin-top: 0;
            padding-top: 0.5rem;
            border-top-left-radius: 0;
            border-top-right-radius: 0;
        }
    }

    & pre::-webkit-scrollbar-thumb {
        background-color: #27272a;
    }

    & pre::-webkit-scrollbar-thumb:hover {
        background-color: #3f3f46;
    }

    & div[data-rehype-pretty-code-fragment] {
        & [data-theme="light"] {
            display: none;
        }
    }
}

.light {
    div[data-rehype-pretty-code-fragment] {
        & [data-theme="dark"] {
            display: none;
        }
    }
}
```css title="./styles/docs.css"
div[data-rehype-pretty-code-title] {
    margin-top: 0;
    background-color: #eef2ff;
    margin-top: 1.5rem;
    border-top-right-radius: 0.5rem;
    border-top-left-radius: 0.5rem;
    font-size: 0.875rem;
    padding: 0.5rem 0.5rem;
    border-bottom: 1px solid #dfe2f5;
    font-weight: 500;

    &+pre {
        margin-top: 0;
        padding-top: 0.5rem;
        border-top-left-radius: 0;
        border-top-right-radius: 0;
    }
}

.dark {
    & div[data-rehype-pretty-code-title] {
        margin-top: 0;
        background-color: #18181b;
        margin-top: 1.5rem;
        border-top-right-radius: 0.5rem;
        border-top-left-radius: 0.5rem;
        font-size: 0.875rem;
        padding: 0.5rem 0.5rem;
        border-bottom: 1px solid #27272a;
        font-weight: 500;

        &+pre {
            margin-top: 0;
            padding-top: 0.5rem;
            border-top-left-radius: 0;
            border-top-right-radius: 0;
        }
    }

    & pre::-webkit-scrollbar-thumb {
        background-color: #27272a;
    }

    & pre::-webkit-scrollbar-thumb:hover {
        background-color: #3f3f46;
    }

    & div[data-rehype-pretty-code-fragment] {
        & [data-theme="light"] {
            display: none;
        }
    }
}

.light {
    div[data-rehype-pretty-code-fragment] {
        & [data-theme="dark"] {
            display: none;
        }
    }
}